﻿using Benefactor.Models.DbModels;
using Benefactor.Services;
using Benefactor.Services.Repositories;
using Bigway.Common.Jobs;
using Bigway.Common.Logging;
using Bigway.Common.Utils;
using Polly;
using Polly.Retry;

namespace Benefactor.Jobs;

public class GenerateVouchersJob(IServiceScopeFactory serviceScopeFactory,
    string systemId) : BaseBackgroundJob<GenerateVouchersJob>(serviceScopeFactory, systemId)
{
    protected override async Task DoJob(IServiceProvider serviceProvider, ILogger<GenerateVouchersJob> logger)
    {
        var voucherRepository = serviceProvider.GetRequiredService<IVoucherRepository>();
        var issuedBenefitsCardRepository = serviceProvider.GetRequiredService<IIssuedBenefitsCardRepository>();
        var benefitsCardService = serviceProvider.GetRequiredService<IBenefitsCardService>();
        var issuedBenefitsCardIdsHasVouchers = await voucherRepository.GetDistinctIssuedBenefitsCardIdsAsync(
            query => query.Where(x => !x.IsDeleted && x.SystemId == SystemId));

        var issuedBenefitsCardsWithoutVouchers = await issuedBenefitsCardRepository.GetAllAsync(
            query =>
            {
                query = query.Where(x => !x.IsDeleted && x.SystemId == SystemId);

                if (issuedBenefitsCardIdsHasVouchers.Count > 0)
                {
                    query = query.Where(x => !issuedBenefitsCardIdsHasVouchers.Contains(x.Id));
                }

                query = query.Includes(x => x.BenefitsCard);
                return query;
            });

        if (issuedBenefitsCardsWithoutVouchers.Count == 0)
        {
            logger.LogInformation("All vouchers are generated");
            return;
        }

        var issuedBenefitsCardWithoutVouchersIds = issuedBenefitsCardsWithoutVouchers
            .Select(x => x.Id).ToList();

        logger.LogInformation(
            "Found the following {IssuedBenefitsCardIdCount} issued benefits cards {IssuedBenefitsCardIds} without vouchers",
            issuedBenefitsCardWithoutVouchersIds.Count,
            string.Join(',', issuedBenefitsCardWithoutVouchersIds)
        );

        var pipeline = new ResiliencePipelineBuilder()
            .AddRetry(new RetryStrategyOptions
            {
                Delay = TimeSpan.FromSeconds(10),
                MaxRetryAttempts = 10
            }) // Add retry using the default options
            .AddTimeout(TimeSpan.FromSeconds(100))
        .Build();

        using var contextUser = new PollyResilienceContextUser();
        var context = contextUser.Context;

        foreach (var issuedBenefitsCard in issuedBenefitsCardsWithoutVouchers)
        {
            logger.LogInformation(
                "Generating vouchers for issued benefits card {IssuedBenefitsCardId} and benefits card {BenefitsCardId}",
                issuedBenefitsCard.Id,
                issuedBenefitsCard.BenefitsCardId);
            var outcome = await pipeline.ExecuteOutcomeAsync<List<Voucher>, object>(
                async (_, _) =>
                {
                    var vouchers = await benefitsCardService.GenerateVouchersAsync(
                        issuedBenefitsCard.BenefitsCard!, issuedBenefitsCard,
                        null, null);

                    return Outcome.FromResult(vouchers);
                },
                context,
                new object()
            );

            var vouchers = outcome.Result ?? [];

            if (vouchers.Count != 0) 
                continue;

            const string message = "Error generating vouchers for issued benefits card {IssuedBenefitsCardId} and benefits card {BenefitsCardId}";
                
            var exception = outcome.Exception;
            if (exception != null)
            {
                logger.LogError(AppLogEvents.BackgroundJob, 
                    exception, 
                    message,
                    issuedBenefitsCard.Id,
                    issuedBenefitsCard.BenefitsCardId
                );
            }
            else
            {
                logger.LogError(message,
                    issuedBenefitsCard.Id,
                    issuedBenefitsCard.BenefitsCardId
                );
            }
        }
    }
}